#include "Common.h"
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#endif
#include <thread>
#include <boost/date_time.hpp>
#include <boost/filesystem.hpp>
#include <shlobj_core.h>
#include "json.hpp"

#define fs boost::filesystem

namespace wsbus {

	void failureWriter(const char *data, int size) 
	{
		LOG(ERROR) << std::string(data, size);
	}

	void initLog(const char *prefix, const char* path, bool console, int level)
	{
		std::string strPath(path);
		std::replace(strPath.begin(), strPath.end(), '\\', '/');
		google::InstallFailureWriter(failureWriter);
		google::InitGoogleLogging(strPath.c_str());
		std::string dir = strPath.substr(0, strPath.find_last_of('/') + 1) + "log/";
#ifdef WIN32
		_mkdir(dir.c_str());
#else
		mkdir(dir.c_str(), 0755);
#endif
		std::string basename = dir + std::string(prefix) + "-";
		google::SetLogDestination(level, basename.c_str());
		FLAGS_alsologtostderr = console;
	}

	void sleepSecond(int second)
	{
		std::this_thread::sleep_for(std::chrono::seconds(second));
	}

	void sleepMillisec(int millisec)
	{
		std::this_thread::sleep_for(std::chrono::milliseconds(millisec));
	}

	time_t currentMillisecond() 
	{
		static const boost::posix_time::ptime EPOCH(boost::gregorian::date(1970, boost::gregorian::Jan, 1));
		return (boost::posix_time::microsec_clock::universal_time() - EPOCH).total_milliseconds();
	}

	std::string getLocalUserPath()
	{
		char path[1024] = { 0 };
		SHGetSpecialFolderPathA(NULL, path, CSIDL_APPDATA, TRUE);
		return std::string(path);
	}

	bool createDirectories(const std::string &path)
	{
		if (boost::filesystem::exists(path)) {
			return true;
		}
		return boost::filesystem::create_directories(path);
	}

	std::string runDir()
	{
		char buffer[MAX_PATH];
		::GetModuleFileNameA(NULL, buffer, sizeof buffer);
		std::string str(buffer);
		std::replace(str.begin(), str.end(), '/', '\\');
		return str.substr(0, str.find_last_of('\\'));
	}

	std::string getBusUserSettingPath()
	{
		auto settingPath = fs::path(getLocalUserPath());
		settingPath.append("CubeBus");
		createDirectories(settingPath.string());
		settingPath.append("setting.json");
		return settingPath.string();
	}

	std::string getBusUserSessionIdPath()
	{
		auto settingPath = fs::path(getLocalUserPath());
		settingPath.append("CubeBus");
		createDirectories(settingPath.string());
		settingPath.append("sessionid");
		return settingPath.string();
	}

	std::string pathNormalize(const std::string &path)
	{
		fs::path p(path);
		return p.make_preferred().string();
	}

	std::string pathNormalize(std::initializer_list<std::string> names)
	{
		fs::path p;
		for (auto &name : names) {
			p.append(name);
		}
		return p.make_preferred().string();
	}

	void saveBusPort(unsigned short port)
	{
		try {
			std::string settingPath = getBusUserSettingPath();
			if (!fs::exists(settingPath)) {
				fs::ofstream ofs(settingPath);
				nlohmann::json j;
				j["port"] = port;
				ofs << j.dump(4);
			}
			else {
				fs::ifstream ifs(settingPath);
				nlohmann::json j;
				try {
					ifs >> j;
					if (j["port"].get<int>() == port) {
						return;
					}
				}
				catch (std::exception &err) {
					LOG(WARNING) << "read json error:" << err.what();
				}

				j["port"] = port;
				fs::ofstream ofs(settingPath);
				ofs << j.dump(4);
			}
		}
		catch (fs::filesystem_error &err) {
			LOG(ERROR) << "save port file error:" << err.what();
		}
		catch (std::exception &err) {
			LOG(ERROR) << "save port error:" << err.what();
		}
		catch (...) {
			LOG(ERROR) << "save port error unkown";
		}
	}

	unsigned short getBusPort()
	{
		unsigned short port = 0;
		std::string settingPath = getBusUserSettingPath();
		fs::ifstream ifs(settingPath);
		if (ifs.is_open()) {
			nlohmann::json j;
			try {
				ifs >> j;
				port = static_cast<unsigned short>(j["port"].get<int>());
			}
			catch (std::exception &err) {
				LOG(WARNING) << "read json error:" << err.what();
			}
		}
		return port;
	}

	unsigned long getCurrentProcessId()
	{
		return GetCurrentProcessId();
	}

	int getCurrentSessionId()
	{
		unsigned long sessionId;
		if (ProcessIdToSessionId(GetCurrentProcessId(), &sessionId)) {
			return (int)sessionId;
		}
		return -1;
	}

}
